package com.zhtools.excel.reader;

import com.zhtools.LoggerUtils;
import com.zhtools.excel.Column;
import com.zhtools.excel.ExcelException;
import com.zhtools.excel.TypeHandler;
import com.zhtools.excel.handler.TypeHandlerManager;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.poi.ss.usermodel.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;

public abstract class SimpleExcelReader<T> implements ExcelReader<T> {
    private static final Logger LOGGER = LoggerFactory.getLogger(SimpleExcelReader.class);

    private static final String OPEN_EXCEL_FAIL = "打开Excel失败";
    private static final String FILEPATH_NOT_EMPTY = "filePath参数不能为空";
    private static final String SHEETINDEX_PARAM_ERROR = "sheetIndex参数必须大于等于0";
    private static final String READ_ROW_FAIL = "读取%s行,%s列失败!";
    private static final String NOT_FIND_TYPEHANDLER = "没有找到属性{}对应的类型处理器(TypeHandler)";

    @Override
    public List<T> read(String filePath, int sheetIndex) {
        if (StringUtils.isEmpty(filePath)) {
            throw new IllegalArgumentException(FILEPATH_NOT_EMPTY);
        }
        if (sheetIndex < 0) {
            throw new IllegalArgumentException(SHEETINDEX_PARAM_ERROR);
        }

        File file = new File(filePath);
        List<T> result;
        InputStream inputStream = null;
        try {
            inputStream = FileUtils.openInputStream(file);
            result = read(inputStream, sheetIndex);
        } catch (IOException e) {
            throw new ExcelException(OPEN_EXCEL_FAIL, e);
        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
        return result;
    }

    @Override
    public List<T> read(File file, int sheetIndex) {
        if (sheetIndex < 0) {
            throw new IllegalArgumentException(SHEETINDEX_PARAM_ERROR);
        }

        List<T> result;
        InputStream inputStream = null;
        try {
            inputStream = FileUtils.openInputStream(file);
            result = read(inputStream, sheetIndex);
        } catch (IOException e) {
            throw new ExcelException(OPEN_EXCEL_FAIL, e);
        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
        return result;
    }

    @Override
    public List<T> read(InputStream inputStream, int sheetIndex) {
        if (sheetIndex < 0) {
            throw new IllegalArgumentException(SHEETINDEX_PARAM_ERROR);
        }

        Workbook workbook = null;
        try {
            workbook = WorkbookFactory.create(inputStream);
        } catch (Exception e) {
            throw new ExcelException(OPEN_EXCEL_FAIL, e);
        }
        Sheet sheet = workbook.getSheetAt(sheetIndex);

        List<T> result = new ArrayList<>();
        int totalRows = sheet.getPhysicalNumberOfRows();//获取总行数
        if (totalRows <= 1) {//除了表头第一行以外没有其他行
            return result;
        }
        for (int rowIndex = 1; rowIndex <= totalRows; rowIndex++) {
            Row row = sheet.getRow(rowIndex);
            result.add(readRow(row, rowIndex));
        }
        return result;
    }

    private T readRow(Row row, int rowIndex) {
        if (row == null) {
            return null;
        }
        Class<T> clazz = null;
        Type t = this.getClass().getGenericSuperclass();
        if (t instanceof ParameterizedType) {
            Type[] p = ((ParameterizedType) t).getActualTypeArguments();
            clazz = (Class<T>) p[0];
        }
        int cellIndex = 0;
        T object = null;
        try {
            object = clazz.newInstance();
            int totalCells = row.getPhysicalNumberOfCells();
            for (int i = 0; i < totalCells; i++) {
                Cell cell = row.getCell(i);
                Column column = getColumn(i);
                if (column == null) {
                    continue;
                }
                setValue(cell, object, column);
                cellIndex = cellIndex + 1;
            }
        } catch (Exception e) {
            e.printStackTrace();
            String error_info = String.format(READ_ROW_FAIL, rowIndex, cellIndex);
            LOGGER.error(error_info, e);
        }
        return object;
    }

    private void setValue(Cell cell, Object object, Column column) throws Exception {
        Field field = object.getClass().getDeclaredField(column.getField());
        String methodName = column.seterMethodName();
        Method method = object.getClass().getDeclaredMethod(methodName, field.getType());

        TypeHandler typeHandler = column.getTypeHandler();
        if (typeHandler == null) {
            typeHandler = TypeHandlerManager.getTypeHandler(field.getType());
        }
        if (typeHandler == null) {
            String msg = LoggerUtils.getLoggerMsg(NOT_FIND_TYPEHANDLER,column.getField());
            System.out.println(msg);
            LOGGER.warn(msg);
            return;
        }
        method.invoke(object, typeHandler.getValue(cell.getStringCellValue()));
    }

    private Column getColumn(int cellIndex) {
        List<Column> columns = columns();
        if (columns == null || columns.size() == 0) {
            return null;
        }
        Column column = null;
        for (Column reader : columns) {
            if (cellIndex == reader.getIndex()) {
                column = reader;
                break;
            }
        }
        return column;
    }

    protected abstract List<Column> columns();
}
